<!DOCTYPE HTML>
<html>
<head>
	<meta charset="utf-8">
	<title>Tree 树形控件</title>
	<link rel="stylesheet" href="../static/css/element2.css">
	<script src="../static/js/vue.global.prod.js"></script>
	<script src="../static/js/element-plue2.js"></script>
	<script src="../static/js/icons-vue.js"></script>
	<link rel="stylesheet" href="../static/css/com.css">
<style>
.is-red > .el-tree-node__content{color: red;}
</style>
</head>
<body>
<div id="app" class="">
	<el-divider content-position="left">基础用法</el-divider><!-- =============================================================== -->
	<p class="desc">基础的树形结构展示</p>
	<el-tree :data="arr_tree" :props="defaultProps" @node-click="handleNodeClick"></el-tree>
	
	<el-divider content-position="left">可选择可禁用</el-divider><!-- =============================================================== -->
	<p class="desc">适用于需要选择层级时使用。</p>
	<p class="desc">节点的复选框可以设置为禁用。在示例中，通过 disabled 设置禁用状态。 相应的复选框已禁用，不能点击。</p>
	<el-tree :data="arr_tree" :props="defaultProps" @check-change="handleCheckChange" show-checkbox></el-tree>
	
	<el-divider content-position="left">懒加载自定义叶子节点</el-divider><!-- =============================================================== -->
	<p class="desc">由于在点击节点时才进行该层数据的获取，默认情况下 Tree 无法预知某个节点是否为叶子节点， 所以会为每个节点添加一个下拉按钮，如果节点没有下层数据，则点击后下拉按钮会消失。</p>
	<p class="desc">同时，你也可以提前告知 Tree 某个节点是否为叶子节点，从而避免在叶子节点前渲染下拉按钮。</p>
	<el-tree :props="defaultProps2" :load="loadNode" lazy show-checkbox></el-tree>
	
	<el-divider content-position="left">默认展开以及默认选中</el-divider><!-- =============================================================== -->
	<p class="desc">可将 Tree 的某些节点设置为禁用状态</p>
	<p class="desc">分别通过 default-expanded-keys 和 default-checked-keys 设置默认展开和默认选中的节点。 需要注意的是，此时必须设置 node-key， 其值为节点数据中的一个字段名，该字段在整棵树中是唯一的。</p>
	<el-tree :data="arr_tree" :props="defaultProps" node-key="id" :default-expanded-keys="[1, 8]" :default-checked-keys="[2]" show-checkbox></el-tree>
	
	<el-divider content-position="left">树节点的选择</el-divider><!-- =============================================================== -->
	<p class="desc">本例展示如何获取和设置选中节点。 获取和设置各有两种方式：通过 node 或通过 key。 如果需要通过 key 来获取或设置，则必须设置 node-key。</p>
	<el-tree ref="treeRef" :data="arr_tree" :props="defaultProps" node-key="id" default-expand-all highlight-current show-checkbox></el-tree>
	<div class="buttons">
	    <el-button @click="getCheckedNodes">获取节点</el-button>
	    <el-button @click="getCheckedKeys">获取节点id</el-button>
	    <el-button @click="setCheckedNodes">设置指定节点为勾选状态</el-button>
	    <el-button @click="setCheckedKeys">设置指定节点为勾选状态2</el-button>
	    <el-button @click="resetChecked">重置</el-button>
	  </div>
	
	<el-divider content-position="left">自定义节点内容</el-divider><!-- =============================================================== -->
	<p class="desc">节点的内容支持自定义，可以在节点区添加按钮或图标等内容</p>
	<p class="desc">可以通过两种方法进行树节点内容的自定义：render-content 和 scoped slot。 使用 render-content 指定渲染函数，该函数返回需要的节点区内容即可。 渲染函数的用法请参考 Vue 文档。 使用 scoped slot 会传入两个参数 node 和 data，分别表示当前节点的 Node 对象和当前节点的数据。 注意：由于 jsfiddle 不支持 JSX 语法，所以render-content示例在 jsfiddle 中无法运行。 但是在实际的项目中，只要正确地配置了相关依赖，就可以正常运行。</p>
	<div class="custom-tree-container">
		<p>Using render-content</p>
		<el-tree ref="treeRef2" :data="arr_tree" show-checkbox node-key="id" default-expand-all :expand-on-click-node="false" :render-content="renderContent"></el-tree>
		
		<p>Using scoped slot（推荐）</p>
		<el-tree :data="arr_tree" show-checkbox node-key="id" default-expand-all :expand-on-click-node="false" @node-click="nodeClick">
			<template #default="{ node, data }">
				<span class="custom-tree-node">
					<span>{{ node.label }}</span>
					<span style="padding-top: 4px;">
						<a style="position: relative;top: 2px;" @click="nodeUpdate(data)"> <Edit style="width: 1em; height: 1em; margin-left: 12px"></Edit> </a>
						<a style="position: relative;top: 2px;" @click="nodeAppend(data)"> <Plus style="width: 1em; height: 1em; margin-left: 12px"></Plus> </a>
						<a style="position: relative;top: 2px;" @click="nodeRemove(node, data)"> <Minus style="width: 1em; height: 1em; margin-left: 12px"></Minus> </a>
					</span>
				</span>
			</template>
		</el-tree>
		
	</div>
	
	<el-divider content-position="left">自定义节点类名</el-divider><!-- =============================================================== -->
	<p class="desc">节点的类名支持自定义。</p>
	<p class="desc">使用 props.class 来建立节点的类名。</p>
	<el-tree :data="arr_tree" :props="defaultProps3" node-key="id" @check-change="handleCheckChange" show-checkbox></el-tree>
	
	<el-divider content-position="left">树节点过滤</el-divider><!-- =============================================================== -->
	<p class="desc">树节点是可以被过滤的</p>
	<p class="desc">调用 Tree 实例对象的 filter 方法来过滤树节点。 方法的参数就是过滤关键字。 需要注意的是，此时需要设置 filter-node-method 属性，其值为过滤函数。</p>
	<el-input v-model="filterText" placeholder="Filter keyword"></el-input>
	<el-tree ref="treeRef3" class="filter-tree" :data="arr_tree" :props="defaultProps" default-expand-all :filter-node-method="filterNode"></el-tree>
	
	<el-divider content-position="left">手风琴模式</el-divider><!-- =============================================================== -->
	<p class="desc">对于同一级的节点，每次只能展开一个</p>
	<el-tree :data="arr_tree" :props="defaultProps" accordion @node-click="handleNodeClick"></el-tree>
	
	<el-divider content-position="left">可拖拽节点</el-divider><!-- =============================================================== -->
	<p class="desc">通过 draggable 属性可让节点变为可拖拽</p>
	<el-tree :data="arr_tree" :props="defaultProps" draggable default-expand-all node-key="id" @node-click="handleNodeClick"
		:allow-drop="allowDrop"
		:allow-drag="allowDrag"
		@node-drag-start="handleDragStart"
		@node-drag-enter="handleDragEnter"
		@node-drag-leave="handleDragLeave"
		@node-drag-over="handleDragOver"
		@node-drag-end="handleDragEnd"
		@node-drop="handleDrop"
	></el-tree>
	
	
</div>

</body>
<script>
const Main = {
	data() {
		return {
			Icons: ElementPlusIconsVue,
			defaultProps: {
				children: 'children',
				label: 'label',
				disabled: 'disabled',
			},
			defaultProps2: {
				children: 'children',
				label: 'label',
				isLeaf: 'leaf',
			},
			defaultProps3: {
				children: 'children',
				label: 'label',
				class: this.isRed,
			},
			filterText: '',
			arr_tree: [
				{
					id: 1,
					label: '湖北省',
					is_red: true,
					children: [
						{
							id: 2,
							label: '武汉市',
							children: [{id: 3, label: '汉口区',},{id: 4, label: '武昌区',},{id: 40, label: '江岸区',},],
						},
						{
							id: 5,
							label: '宜昌市',
							children: [{id: 6, label: '武陵区',},],
							disabled: true,
						},
					],
				},
				{
					id: 8,
					label: '河南省',
					children: [
						{
							id: 9,
							label: '郑州市',
							is_red: true,
							children: [{id: 10, label: '二七区',},{id: 11, label: '金水区',},],
						},
						{
							id: 12,
							label: '开封市',
							children: [{id: 13, label: '北郊区',},],
						},
					],
				},
			],
		}
	},
	watch: {
		filterText: function(val){
			this.$refs.treeRef3.filter(val);
		},
		
	},
	computed: {
		// isRed0(){
		// 	return function(data, node){
		// 		// console.log(data)
		// 		if(data.is_red){
		// 			return "is-red";
		// 		}else{
		// 			return null;
		// 		}
		// 	}
		// }
	},
	methods:{
		fn(){
			this.message = "欢迎来到HTML中文网。";
		},
		hello(par){
			this.message = "参数值为：" + par;
		},
		isRed(data, node){
			// console.log(data, node)
			// return "red";
			if(data.is_red){
				return "is-red";
			}else{
				return null;
			}
		},
		handleNodeClick(node){
			console.log(node);
		},
		handleCheckChange(data, checked, indeterminate){
			console.log(data, checked, indeterminate);
		},
		
		loadNode(node, resolve){
			// console.log(node);
			// console.log(node.data.id);
			// console.log(resolve);
			if(node.level === 0){
				return resolve([{id:1, label: '湖北省' }, {id:2, label: '河南省' }])
			}
			if(node.data.id == 1){
				setTimeout(() => {
					let arr_tree = [{id:3, label: '武汉市'}, {id:4, label: '宜昌市', leaf: true, }];
					return resolve(arr_tree);
				}, 200);
			}
			if(node.data.id == 2){
				setTimeout(() => {
					let arr_tree = [{id:5, label: '郑州市'}, {id:6, label: '开封市'}];
					return resolve(arr_tree);
				}, 200);
			}
		},
		//获取节点
		getCheckedNodes(){
			console.log(this.$refs.treeRef.getCheckedNodes(false, false));
		},
		//获取节点id
		getCheckedKeys(){
			console.log(this.$refs.treeRef.getCheckedKeys(false));
		},
		//设置指定节点为勾选状态
		setCheckedNodes(){
			this.$refs.treeRef.setCheckedNodes([
				{id: 11},
				{id: 12},
			])
		},
		//设置指定节点为勾选状态2
		setCheckedKeys(){
			this.$refs.treeRef.setCheckedKeys([3, 6]);
		},
		//重置
		resetChecked(){
			// console.log(this);
			// console.log(this.$refs.treeRef);
			this.$refs.treeRef.setCheckedKeys([]);
		},
		
		//增、删节点
		renderContent(h, {node, data, store}){
			// console.log(h);
			// console.log(node);
			// console.log(data);
			// console.log(store);
			// console.log(this.$refs);
			// console.log(this.$refs.treeRef2);
			return h(
				'span',
				{class: 'custom-tree-node',},
				h('span', null, node.label),
				h(
					'span',
					{style: 'margin-left: 12px',},
					h(
						'a',
						{
							onClick: () => {
								console.log(data);
								this.$prompt("", "给 " + data.label + " 添加子级", {
									confirmButtonText: '确定',
									cancelButtonText: '取消',
									inputPattern: /.+/,
									inputErrorMessage: '不能为空。',
								}).then(({value})=>{
									this.$refs.treeRef2.append({label: value}, data.id);
									//这里异步添加类目，然后懒加载出来
									// this.$message.success("输入了：" + value);
								}).catch(()=>{
									this.$message.info("你取消了输入。");
								})
								// this.$refs.treeRef2.append(data, 11);
							}
						},
						'+ '
					),
					h(
						'a',
						{
							onClick: () => {
								// this.$refs.treeRef2.remove(node)
								this.$alert('', "确定删除 " + data.label + " 节点吗？", {
									showCancelButton: true,
									confirmButtonText: '确定',//默认为OK
									cancelButtonText: '取消',
									callback: (action) => {
										console.log(action);
										if(action == "confirm"){
											this.$refs.treeRef2.remove(node)
											// this.$message.success("你点击了确定。");
										}else if(action == "cancel"){
											this.$message.warning("你取消了操作。");
										}
									},
								});
							},
							style: 'margin-left: 12px',
						},
						'-'
					)
				)
			)
		},
		
		//节点 - 点击
		nodeClick(node){
			console.log("节点 - 点击：", node.label);
		},
		//节点 - 增
		nodeAppend(data){
			// console.log(data);
			this.$prompt("", "1、给 " + data.label + " 添加子级", {
				confirmButtonText: '确定',
				cancelButtonText: '取消',
				inputPattern: /.+/,
				inputErrorMessage: '不能为空。',
			}).then(({value})=>{
				this.$refs.treeRef2.append({label: value}, data.id);
				//这里异步添加类目，然后懒加载出来
				// this.$message.success("输入了：" + value);
			}).catch(()=>{
				this.$message.info("你取消了输入。");
			})
		},
		//节点 - 删
		nodeRemove(node, data){
			this.$alert('', "2、确定删除 " + data.label + " 节点吗？", {
				showCancelButton: true,
				confirmButtonText: '确定',//默认为OK
				cancelButtonText: '取消',
				callback: (action) => {
					console.log(action);
					if(action == "confirm"){
						this.$refs.treeRef2.remove(node)
						// this.$message.success("你点击了确定。");
					}else if(action == "cancel"){
						this.$message.warning("你取消了操作。");
					}
				},
			});
		},
		//节点 - 改
		nodeUpdate(node){
			console.log("节点 - 改：", node);
			this.$prompt("", "3、修改节点", {
				confirmButtonText: '确定',
				cancelButtonText: '取消',
				inputPlaceholder: '必须输入一个类目名！',
				inputValue: node.label,
				inputPattern: /.+/,
				inputErrorMessage: '不能为空。',
			}).then(({value})=>{
				this.$message.success("你输入的是：" + value);
			}).catch(()=>{
				this.$message.info("你取消了输入。");
			})
		},
		
		//节点过滤方法
		filterNode(value, data){
			if(!value) return true;
			return data.label.includes(value);
		},
		
		//节点拖动
		//节点开始拖拽时触发的事件
		handleDragStart(node, ev){
			console.log('drag start node : ', node);
			console.log('drag start ev : ', ev)
		},
		//拖拽进入其他节点时触发的事件
		handleDragEnter(draggingNode, dropNode, ev){
			// console.log('tree drag enter:', dropNode.label)
		},
		//拖拽离开某个节点时触发的事件
		handleDragLeave(draggingNode, dropNode, ev){
			// console.log('tree drag leave:', dropNode.label)
		},
		//在拖拽节点时触发的事件（类似浏览器的 mouseover 事件）
		handleDragOver(draggingNode, dropNode, ev){
			// console.log('tree drag over:', dropNode.label)
		},
		//拖拽结束时（可能未成功）触发的事件
		handleDragEnd(draggingNode, dropNode, dropType, ev){
			console.log('tree drag end:', dropNode && dropNode.label, dropType)
		},
		//拖拽成功完成时触发的事件
		handleDrop(draggingNode, dropNode, dropType, ev){
			console.log('tree drop:', dropNode.label, dropType)
		},
		//拖拽时判定目标节点能否成为拖动目标位置
		allowDrop(draggingNode, dropNode, dropType){
			console.log('allowDrop:', dropNode.label, dropType)
			if (dropNode.data.label === '武汉市') {
				return dropType !== 'inner'
			} else {
				return true
			}
		},
		//判断节点能否被拖拽
		allowDrag(draggingNode){
			return !draggingNode.data.label.includes('武汉市')
		},
		
		
	},
}

const app = Vue.createApp(Main);
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
	app.component(key, component)
}
app.use(ElementPlus);
app.mount("#app");
</script>
</html>